﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StyleMVVM.Data.Collections
{
	public class WeakCollection<T> : ICollection<T> where T : class
	{
		private readonly List<WeakReference<T>> references;

		public WeakCollection()
		{
			references = new List<WeakReference<T>>();
		}

		public IEnumerator<T> GetEnumerator()
		{
			List<T> enumeratorable = new List<T>();

			for (int i = 0; i < references.Count;)
			{
				T reference;

				if (references[i].TryGetTarget(out reference))
				{
					enumeratorable.Add(reference);

					i++;
				}
				else
				{
					references.RemoveAt(i);
				}
			}

			return enumeratorable.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public void Add(T item)
		{
			references.Add(new WeakReference<T>(item));
		}

		public void Clear()
		{
			references.Clear();
		}

		public bool Contains(T item)
		{
			bool returnValue = false;

			for (int i = 0; i < references.Count;)
			{
				T reference;

				if (references[i].TryGetTarget(out reference))
				{
					if (reference == item)
					{
						returnValue = true;
					}

					i++;
				}
				else
				{
					references.RemoveAt(i);
				}
			}

			return returnValue;
		}

		public void CopyTo(T[] array, int arrayIndex)
		{
			if (array.Length - arrayIndex > Count)
			{
				throw new ArgumentException("Array is to small");
			}

			for (int i = 0; i < references.Count;)
			{
				T reference;

				if (references[i].TryGetTarget(out reference))
				{
					array[i + arrayIndex] = reference;

					i++;
				}
				else
				{
					references.RemoveAt(i);
				}
			}
		}

		public bool Remove(T item)
		{
			bool returnValue = false;

			for (int i = 0; i < references.Count;)
			{
				T reference;

				if (references[i].TryGetTarget(out reference))
				{
					if (!returnValue && reference == item)
					{
						returnValue = true;

						references.RemoveAt(i);
					}
					else
					{
						i++;
					}
				}
				else
				{
					references.RemoveAt(i);
				}
			}

			return returnValue;
		}

		public int Count
		{
			get
			{
				int returnValue = 0;

				for (int i = 0; i < references.Count;)
				{
					T reference;

					if (references[i].TryGetTarget(out reference))
					{
						returnValue++;
						i++;
					}
					else
					{
						references.RemoveAt(i);
					}
				}

				return returnValue;
			}
		}

		public bool IsReadOnly
		{
			get { return false; }
		}
	}
}